/**
 * Copyright 2013 by dragon.
 *
 * File name: JsNative.js
 * Author: dragon
 * Email: fufulove2012@gmail.com
 * Blog: http://blog.csdn.net/xidomlove
 * Version: 1.0.0
 * Date: 2013-10-2 下午4:38:48
 * Description:
 */
(function() {

    //applet handle
    var jsnative = {};

    //the user's options
    var myOptions;

    //Reflection library
    var Reflection = {
        /**
         *get object's public property
         *object = array
         * e.g. getProperty(object,'length');
         *
         */
        getProperty: function(obj, fieldName) {
            var ownerClass = obj.getClass();

            var field = ownerClass.getField(fieldName);

            var property = field.get(obj);

            return property;
        },
        /**
         * e.g. getStaticProperty('java.lang.System','out');
         */
        getStaticProperty: function(className, fieldName) {
            var cls = jsnative.getJavaClass(className);

            var field = cls.getField(fieldName);

            var property = field.get(cls);

            return property;
        },
        /**object = 'hello'
         * e.g. invokeMethod(object,'getBytes',['utf-8'],['java.lang.String'],'java.lang.String')
         *
         */
        invokeMethod: function(obj, methodName, args, argsClass, optionalObjClsName) {
            if (args) {
                this.processArgs(args, argsClass);
            } else {
                args = [];
                argsClass = [];
            }

            var ownerClass;
            if (optionalObjClsName) {
                ownerClass = jsnative.getJavaClass(optionalObjClsName);
            } else if (typeof(obj) == 'string') {
                ownerClass = jsnative.getJavaClass('java.lang.String');
            } else {
                ownerClass = obj.getClass();
            }

            var method = ownerClass.getMethod(methodName, argsClass);

            return jsnative.invokeMethod(method, obj, args);
        },
        /**
         * e.g. invokeStaticMethod('java.lang.System','getProperty',['usr.dir'],['java.lang.String']);
         *
         */
        invokeStaticMethod: function(className, methodName, args, argsClass) {
            if (args) {
                this.processArgs(args, argsClass);
            } else {
                args = [];
                argsClass = [];
            }
            var cls = jsnative.getJavaClass(className);
            var method = cls.getMethod(methodName, argsClass);

            return jsnative.invokeMethod(method, null, args);
        },
        /**
         * create a new object
         *e.g. newInstance('java.lang.String',['hello'],['java.lang.String']);
         */
        newInstance: function(className, args, argsClass) {
            if (args) {
                this.processArgs(args, argsClass);
            } else {
                args = [];
                argsClass = [];
            }
            var cls = jsnative.getJavaClass(className);
            // var cons = cls.getConstructor(argsClass);
            return jsnative.newInstance(cls, argsClass, args);
        },

        getJavaClass: function(className) {
            if (className == 'int') {
                return this.getStaticProperty('java.lang.Integer',
                    'TYPE');
            } else if (className == 'long') {
                return this.getStaticProperty('java.lang.Long',
                    'TYPE');
            } else if (className == 'double') {
                return this.getStaticProperty('java.lang.Double',
                    'TYPE');
            } else if (className == 'float') {
                return this.getStaticProperty('java.lang.Float',
                    'TYPE');
            } else if (className == 'short') {
                return this.getStaticProperty('java.lang.Short',
                    'TYPE');
            } else if (className == 'char') {
                return this.getStaticProperty(
                    'java.lang.Character', 'TYPE');
            } else if (className == 'boolean') {
                return this.getStaticProperty('java.lang.Boolean',
                    'TYPE');
            } else if (className == 'byte') {
                return this.getStaticProperty('java.lang.Byte',
                    'TYPE');
            } else {
                return jsnative.getJavaClass(className);
            }
        },

        /*
         * process argument to adjust Java class name
         */
        processArgs: function(args, argsclass) {
            for (var i = 0; i < argsclass.length; i++) {
                if (argsclass[i] == 'int') {
                    argsclass[i] = this.getStaticProperty('java.lang.Integer',
                        'TYPE');
                    args[i] = jsnative.getJavaInteger(args[i]);
                } else if (argsclass[i] == 'long') {
                    argsclass[i] = this.getStaticProperty('java.lang.Long',
                        'TYPE');
                    args[i] = jsnative.getJavaLong(args[i]);
                } else if (argsclass[i] == 'double') {
                    argsclass[i] = this.getStaticProperty('java.lang.Double',
                        'TYPE');
                    args[i] = jsnative.getJavaDouble(args[i]);
                } else if (argsclass[i] == 'float') {
                    argsclass[i] = this.getStaticProperty('java.lang.Float',
                        'TYPE');
                    args[i] = jsnative.getJavaFloat(args[i]);
                } else if (argsclass[i] == 'short') {
                    argsclass[i] = this.getStaticProperty('java.lang.Short',
                        'TYPE');
                    args[i] = jsnative.getJavaShort(args[i]);
                } else if (argsclass[i] == 'char') {
                    argsclass[i] = this.getStaticProperty(
                        'java.lang.Character', 'TYPE');
                    args[i] = jsnative.getJavaCharacter(args[i]);
                } else if (argsclass[i] == 'boolean') {
                    argsclass[i] = this.getStaticProperty('java.lang.Boolean',
                        'TYPE');
                    args[i] = jsnative.getJavaBoolean(args[i]);
                } else if (argsclass[i] == 'byte') {
                    argsclass[i] = this.getStaticProperty('java.lang.Byte',
                        'TYPE');
                    args[i] = jsnative.getJavaByte(args[i]);
                } else {
                    argsclass[i] = jsnative.getJavaClass(argsclass[i]);
                }
            }
        }
    };

    var myTimer;
    var timerCount;
    var notdeployed = true;

    window.JsNative = {

        //load JsNative lib
        load: function(options) {
            if (options) {
                myOptions = options;
            } else {
                myOptions = {};
            }

            //load deployJAVA
            var oHead = document.getElementsByTagName('HEAD').item(0);

            var oScript = document.createElement("script");

            oScript.type = "text/javascript";

            oScript.src = "http://www.java.com/js/deployJava.js";

            oHead.appendChild(oScript);
            //


            if (myTimer) {} else {
                timerCount = 0;
                myTimer = setInterval(function() {

                    if (timerCount > 100) {
                        clearInterval(myTimer);
                        delete myTimer;

                        if (jsnative.createThread == undefined) {

                            console.log('jsnative load failed!')
                            if (myOptions.loadFailed) {
                                myOptions.loadFailed();
                            }
                        }
                        return;
                    }

                    if (jsnative.createThread) {
                        timerCount = 10000;
                        console.log('jsnative load success!');
                        if (myOptions.loadSuccess) {
							var logger = Reflection.newInstance('com.dragon.log.Logger',[Reflection.getStaticProperty('java.lang.System','out')],['java.io.OutputStream']);
							logger.disableLog();
							Reflection.invokeStaticMethod('com.dragon.log.Logger','setDefaultLogger',[logger],['com.dragon.log.Logger']);
                            myOptions.loadSuccess();
                        }
                    }

                    if (notdeployed && window.deployJava != undefined) {
                        notdeployed = false;
                        var attributes = {
                            id: 'jsnative',
                            code: 'com.dragon.jsnative.Main.class',
                            archive: 'JsNative.jar',
                            width: 1,
                            height: 1
                        };
                        var parameters = {};
                        var tmp = document.write;
                        document.write = function(content) {
                            var div = document.createElement('div');
                            div.innerHTML = content;
                            document.body.appendChild(div.firstChild);
                        }
                        deployJava.runApplet(attributes, parameters, '1.6');
                        document.write = tmp;
                        jsnative = document.jsnative;
                    }

                    timerCount += 1;
                }, 100);
            }
        },
        onAppletStart: function() {
            console.log('applet start');
        },
        onAppletStart: function() {
            console.log('applet start');
        },
        system: {

            //get the OS type
            os: function() {
                var OSName = "unknown OS";
                if (navigator.appVersion.indexOf("Win") != -1)
                    OSName = "Windows";
                if (navigator.appVersion.indexOf("Mac") != -1)
                    OSName = "MacOS";
                if (navigator.appVersion.indexOf("X11") != -1)
                    OSName = "UNIX";
                if (navigator.appVersion.indexOf("Linux") != -1)
                    OSName = "Linux";
                return OSName;
            },

            //execute a system command or a process,e.g. JsNative.system.exec('dir',1000,'gb2312')
            //warning: when specific the timeout the function always block for timeout m seconds, and I don't know why :(
            exec: function(cmd, timeout, charset) {

                if (charset) {} else {
                    charset = 'utf-8';
                }
                var rt = Reflection.invokeStaticMethod('java.lang.Runtime', 'getRuntime');

                if (this.os() == 'Windows') {
                    cmd = 'cmd /c ' + cmd;
                } else {
                    bash = 'bash ' + cmd;
                }
                var jsargs = cmd.split(' ');
                var args = JsNative.system.array('java.lang.String', jsargs.length);
                for (var i = 0; i < jsargs.length; i++) {
                    args.set(i, jsargs[i]);
                }
                var processBuilder = Reflection.newInstance('java.lang.ProcessBuilder', [args.getArray()], ['[Ljava.lang.String;']);
                var process = Reflection.invokeMethod(processBuilder, 'start');
                var running = true;
                if (process) {

                    if (timeout) {
                        JsNative.system.thread.create(function() {
                            JsNative.system.thread.sleep(timeout);
                            if (running) {
                                console.log('process timeout');
                                process.destroy();
                            }
                        });
                    }

                    var exitcode;

                    var maxlen = 8192;
                    var output;
                    var errstr;
                    try {

                        //create thread to read standard output
                        var t = JsNative.system.thread.create(function() {
                            var i;
                            var offset = 0;
                            var ins = process.getInputStream();
                            var arr = JsNative.system.array('byte', maxlen);
                            for (i = 0; i < maxlen; i++) {

                                var len = ins.read(arr.getArray(), offset, maxlen - offset);
                                if (len == -1) {
                                    break;
                                }
                                offset += len;

                            }
                            output = Reflection.newInstance('java.lang.String', [arr.getArray(), 0, offset, charset], [arr.getArray().getClass().getName(), 'int', 'int', 'java.lang.String']);
                        });

                        //create a thread to read the process's error output, to avoid the process blocking
                        JsNative.system.thread.create(function() {

                            var i;
                            var offset = 0;
                            var ins = process.getErrorStream();
                            var arr = JsNative.system.array('byte', maxlen);
                            for (i = 0; i < maxlen; i++) {

                                var len = ins.read(arr.getArray(), offset, maxlen - offset);
                                if (len == -1) {
                                    break;
                                }
                                offset += len;

                            }
                            errstr = Reflection.newInstance('java.lang.String', [arr.getArray(), 0, offset, charset], [arr.getArray().getClass().getName(), 'int', 'int', 'java.lang.String']);
                        });

                        exitcode = process.waitFor();
                        t.jion(3000);
                        running = false;

                    } catch (err) {}
                    return {
                        exitValue: exitcode,
                        output: output,
                        err: errstr
                    };
                } else {
                    return 0;
                }
            },

            // create a Java array object byte class name, e.g.JsNative.system.array('byte', 100)
            array: function(className, len) {
                return new function() {
                    var arr = Reflection.invokeStaticMethod('java.lang.reflect.Array', 'newInstance', [Reflection.getJavaClass(className), len], ['java.lang.Class', 'int']);
                    var clsname = className;
                    var capacity = len;
                    this.set = function(index, val) {
                        Reflection.invokeStaticMethod('java.lang.reflect.Array', 'set', [arr, index, val], ['java.lang.Object', 'int', 'java.lang.Object']);
                    };

                    this.get = function(index) {
                        return Reflection.invokeStaticMethod('java.lang.reflect.Array', 'get', [arr, index], ['java.lang.Object', 'int']);
                    };

                    this.getArray = function() {
                        return arr;
                    };
                };
            },
            string: {
                getBytes: function(str, charset) {
                    return Reflection.invokeMethod(str, 'getBytes', [charset], ['java.lang.String']);
                }
            },

			// deprecated, even in a new thread when calling JavaScript function will cause the browser blocked!
            thread: {

                //create a new thread and run the runnable function,e.g. JsNative.system.thread.create(function(){JsNative.system.thread.sleep(1000); console.log(1)});
                create: function(runnable) {
                    return new function() {
                        var t;
                        if (typeof(runnable == 'function')) {
                            t = jsnative.createThread({
                                run: runnable
                            });
                        }

                        // wait for thread exit
                        this.jion = function(millis) {
                            if (millis) {
                                t.join(millis);
                            } else {
                                t.join();
                            }
                        };
                    };
                },

                //pause the current thread execute m seconds
                sleep: function(millis) {
                    Reflection.invokeStaticMethod('java.lang.Thread', 'sleep', [millis], ['long']);
                }
            }
        },
        io: {

            //file op, e.g. f=JsNative.io.file('f :  \\ gyfhh.txt',true)
            file: function(path, isappend) {
                return new function() {
                    var append;
                    if (isappend) {
                        append = true;
                    } else {
                        append = false;
                    }
                    var f = Reflection.newInstance('java.io.File', [path], ['java.lang.String']);

                    this.exists = function() {
                        return Reflection.invokeMethod(f, 'exists');
                    };
                    this.getName = function() {
                        return Reflection.invokeMethod(f, 'getName');
                    };
                    this.getPath = function() {
                        return Reflection.invokeMethod(f, 'getPath');
                    };
                    this.getAbsolutePath = function() {
                        return Reflection.invokeMethod(f, 'getAbsolutePath');
                    };
                    this.getCanonicalPath = function() {
                        return Reflection.invokeMethod(f, 'getCanonicalPath');
                    };
                    this.getFreeSpace = function() {
                        return Reflection.invokeMethod(f, 'getFreeSpace');
                    };
                    this.getTotalSpace = function() {
                        return Reflection.invokeMethod(f, 'getTotalSpace');
                    };

                    this.length = function() {
                        return Reflection.invokeMethod(f, 'length');
                    };
                    this.list = function() {
                        return Reflection.invokeMethod(f, 'list');
                    };
                    this.listFiles = function() {
                        return Reflection.invokeMethod(f, 'listFiles');
                    };
                    this.mkdir = function() {
                        return Reflection.invokeMethod(f, 'mkdir');
                    };
                    this.mkdirs = function() {
                        return Reflection.invokeMethod(f, 'mkdirs');
                    };
                    this.lastModified = function() {
                        return Reflection.invokeMethod(f, 'lastModified');
                    };
                    this.isHidden = function() {
                        return Reflection.invokeMethod(f, 'isHidden');
                    };
                    this.isFile = function() {
                        return Reflection.invokeMethod(f, 'isFile');
                    };
                    this.isDirectory = function() {
                        return Reflection.invokeMethod(f, 'isDirectory');
                    };
                    this.delete = function() {
                        return Reflection.invokeMethod(f, 'delete');
                    };
                    this.renameTo = function(newName) {
                        return Reflection.invokeMethod(f, 'renameTo', [Reflection.newInstance('java.io.File', [newName], ['java.lang.String'])], ['java.io.File']);
                    };
                    this.getParent = function() {
                        return Reflection.invokeMethod(f, 'getParent');
                    };
                    var input;
                    var output;
                    this.readString = function(maxlength, charset) {
                        if (maxlength) {} else {
                            maxlength = 8192;
                        }
                        if (input) {} else {
                            input = Reflection.newInstance('java.io.FileInputStream', [f], ['java.io.File']);
                        }

                        if (charset) {} else {
                            charset = 'utf-8';
                        }
                        var arr = JsNative.system.array('byte', maxlength);
                        try {
                            var len = input.read(arr.getArray());
                            return Reflection.newInstance('java.lang.String', [arr.getArray(), 0, len, charset], [arr.getArray().getClass().getName(), 'int', 'int', 'java.lang.String']);

                        } catch (err) {
                            console.log(err);
                            return 0;
                        }
                    };

                    this.writeString = function(str) {
                        if (output) {} else {
                            output = Reflection.newInstance('java.io.FileOutputStream', [f, append], ['java.io.File', 'boolean']);
                        }
                        output.write(JsNative.system.string.getBytes(str, 'utf-8'));
                    };

                    var reader;
                    this.readLine = function() {
                        if (input) {} else {
                            input = Reflection.newInstance('java.io.FileInputStream', [f], ['java.io.File']);
                        }
                        if (reader) {} else {
                            var tmp = Reflection.newInstance('java.io.InputStreamReader', [input, 'UTF-8'], ['java.io.InputStream', 'java.lang.String']);
                            reader = Reflection.newInstance('java.io.BufferedReader', [tmp], ['java.io.Reader']);
                        }
                        try {
                            return reader.readLine();
                        } catch (err) {
                            console.log(err);
                            return 0;
                        }
                    };

                    this.writeLine = function(str) {
                        this.writeString(str + '\n');
                    };

                    this.close = function() {
                        if (input) {
                            input.close();
                        }
                        if (output) {
                            output.close();
                        }
                        if (reader) {
                            reader.close();
                        }
                    };
                };
            }
        },
        net: {
            //create a socket and return the socket object if success,timeout is optional
            socket: function(ip, port,timeout) {
                return new function() {
                    var socketcls = 'java.net.Socket';
                    var strcls = 'java.lang.String';
                    var intcls = 'int';
					var sacls = 'java.net.SocketAddress';
					var iacls = 'java.net.InetAddress';
                    var hsocket = Reflection.newInstance(socketcls);
					
					var addr = Reflection.newInstance('java.net.InetSocketAddress',[ip,port],[strcls,intcls]);
					if(timeout){
						Reflection.invokeMethod(hsocket,'connect',[addr,timeout],[sacls,intcls]);
					}else{
						Reflection.invokeMethod(hsocket,'connect',[addr],[sacls]);
					}
                    var houtput = Reflection.newInstance(
                        'java.io.DataOutputStream', [hsocket
                            .getOutputStream()
                        ], ['java.io.OutputStream']);

                    var hinput = Reflection.newInstance('java.io.DataInputStream', [hsocket.getInputStream()], ['java.io.InputStream']);

                    var tmp = Reflection.newInstance('java.io.InputStreamReader', [hinput, 'UTF-8'], ['java.io.InputStream', strcls]);
                    var reader = Reflection.newInstance('java.io.BufferedReader', [tmp], ['java.io.Reader']);
                    delete tmp;
                    this.close = function() {
                        hinput.close();
                        houtput.close();
                        hsocket.close();
                        reader.close();
                    };

                    this.isClosed = function() {
                        return hsocket.isClosed();
                    };

                    this.isConnected = function() {
                        return hsocket.isConnected();
                    };

                    //read a string,default length 8K,charset utf-8,e.g:readString(1024,'gbk')
                    this.readString = function(maxlength, charset) {
                        if (maxlength) {} else {
                            maxlength = 8192;
                        }
                        if (charset) {} else {
                            charset = 'utf-8';
                        }
                        var arr = JsNative.system.array('byte', maxlength);
                        try {
                            var len = hinput.read(arr.getArray());
                            return Reflection.newInstance('java.lang.String', [arr.getArray(), 0, len, charset], [arr.getArray().getClass().getName(), 'int', 'int', 'java.lang.String']);

                        } catch (err) {
                            return -1;
                        }
                    };

                    this.writeString = function(str) {
                        houtput.writeBytes(str);
                    };

                    this.readLine = function() {
                        try {
                            return reader.readLine();
                        } catch (err) {
                            console.log(err);
                            return -1;
                        }
                    };

                    this.writeLine = function(str) {
                        this.writeString(str + '\n');
                    };

                    this.readByte = function() {
                        return hinput.readByte();
                    };

                    this.readDouble = function() {
                        return hinput.readDouble();
                    };

                    this.readFloat = function() {
                        return hinput.readFloat();
                    };

                    this.readInt = function() {
                        return hinput.readInt();
                    };

                    this.readLong = function() {
                        return hinput.readLong();
                    };

                    this.readShort = function() {
                        return hinput.readShort();
                    };

                    this.writeByte = function(b) {
                        houtput.writeByte(b);
                    };

                    this.writeDouble = function(d) {
                        houtput.writeDouble(d);
                    };

                    this.wrtieFloat = function(f) {
                        houtput.writeFloat(f);
                    };

                    this.writeInt = function(i) {
                        houtput.writeInt(i);
                    };

                    this.writeLong = function(l) {
                        houtput.writeLong(l);
                    };

                    this.writeShort = function(s) {
                        houtput.writeShort(s);
                    };
                };
            },
            http: {

                //usage:dl=JsNative.net.http.download('http://localhost/a.exe', 'F:\\daadffds.exe')
                download: function(url, savePath) {
                    return new function() {
                        var dl = Reflection.newInstance('com.dragon.jaxel.xtp.HttpDownloader', [url, savePath], ['java.lang.String', 'java.lang.String']);

                        var size;

                        //get the server's file length, so you can specific connection count on start according to the file length
                        this.getFileLength = function() {
                            if (size) {} else {
                                size = dl.getSize();
                            }
                            return size;
                        };
						
						this.getProgress = function() {
							return dl.getProgress();
						};

                        var timer;
                        var opt;

                        //stop downloading
                        this.stop = function() {
                            dl.stop();
							clearInterval(timer);
                        };

                        //usage:
                        //start({
                        //connectionCount:6,
                        //updateInterval:1000,
                        //onProgress:function(progress){console.log(progress)},
                        //onSuccess:function(){console.log('download sucess')},
                        //onFailed:function(){console.log('download failed')}
                        //})
                        this.start = function(options) {
                            opt = options;
                            if (opt && opt.connectionCount) {
								Reflection.invokeMethod(dl, 'start', [opt.connectionCount], ['int']);
							} else {
                                opt = {};
								Reflection.invokeMethod(dl, 'start');
                            }

                            if (opt.onProgress || opt.onSuccess || opt.onFailed) {
                                if (opt.updateInterval) {} else {
                                    opt.updateInterval = 1000;
                                }
                                timer = setInterval(function() {
                                    var progress = dl.getProgress();
                                    if (!dl.isDownLoading()) {
                                        //down load finished
                                        clearInterval(timer);
                                        if (progress >= dl.getSize()) {
                                            //down load ok
                                            if (opt.onSuccess) {
                                                opt.onSuccess();
                                            }
                                        } else {
                                            //down load fail
                                            if (opt.onFailed) {
                                                opt.onFailed();
                                            }
                                        }
                                    } else {
                                        opt.onProgress(progress);
                                    }
                                }, opt.updateInterval);
                            }
                        };

                        this.pause = function() {
							dl.pause();
							clearInterval(timer);
                        };

                        this.resume = function() {
                            Reflection.invokeMethod(dl, 'resume');

                            if (opt.onProgress || opt.onSuccess || opt.onFailed) {
                                if (opt.updateInterval) {} else {
                                    opt.updateInterval = 1000;
                                }
                                timer = setInterval(function() {
                                    var progress = dl.getProgress();
                                    if (!dl.isDownLoading()) {
                                        //down load finished
                                        clearInterval(timer);
                                        if (progress >= dl.getSize()) {
                                            //down load ok
                                            if (opt.onSuccess) {
                                                opt.onSuccess();
                                            }
                                        } else {
                                            //down load fail
                                            if (opt.onFailed) {
                                                opt.onFailed();
                                            }
                                        }
                                    } else {
                                        opt.onProgress(progress);
                                    }
                                }, opt.updateInterval);
                            }
                        };

                    };
                }
            }
        }
    };

    //for console log byte
    window.javalog = new function() {

        var buffer = Array(4);
        var buflen = 0;
        var state = 0;
        this.log = function(arg0) {
            var byte1 = 0xff & arg0;
            console.log(byte1);
            if (state == 0) {
                buflen = 1;
                buffer[0] = byte1;
                if (byte1 < 0x80) {
                    console.log(String.fromCharCode(byte1));
                    state = 0;
                } else if (byte1 >= 0xC2 && byte1 < 0xE0) {
                    state = 1;
                } else if (byte1 >= 0xE0 && byte1 < 0xF0) {
                    state = 2;
                } else if (byte1 >= 0xF0 && byte1 < 0xF5) {
                    state = 3;
                }
            } else {
                buffer[buflen] = byte1;
                if (buflen < state) {
                    buflen += 1;
                } else {
                    switch (state) {
                        case 1:
                            console.log(String.fromCharCode(((buffer[0] & 0x1F) << 6) + (buffer[1] & 0x3F)));
                            break;
                        case 2:
                            console.log(String.fromCharCode(((buffer[0] & 0xFF) << 12) + ((buffer[1] & 0x3F) << 6) + (buffer[2] & 0x3F)));
                            break;
                        case 3:
                            var codepoint = ((buffer[0] & 0x07) << 18) + ((buffer[1] & 0x3F) << 12) + ((buffer[2] & 0x3F) << 6) + (buffer[3] & 0x3F);
                            codepoint -= 0x10000;
                            console.log(String.fromCharCode((codepoint >> 10) + 0xD800, (codepoint & 0x3FF) + 0xDC00));
                            break;
                    }
                    state = 0;
                }
            }
        };

    };
})();
